package se.lnu.axez;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import se.lnu.axez.animation.Animation;
import android.os.SystemClock;
import android.view.MotionEvent;

public abstract class ControlBase implements IControl {
	private final Transformation transformation;
	private final IVisual visual;
	private final IBoundingShape boundingShape;
	private List<Animation<IControl>> activeAnimations;
	private TouchEventTracker touchEventTracker;
	private String tag;
	private OnClickListener mOnClickListener;

	protected ControlBase() {
		this(null);
	}
	
	protected ControlBase(IVisual visual) {
		this(visual, null);
	}
	
	protected ControlBase(IVisual visual, IBoundingShape boundingShape) {
		this.visual = visual;
		this.boundingShape = boundingShape;
		this.transformation = new Transformation();
	}

	public final void addAnimation(Animation<IControl> animation) {
		if (animation == null) {
			return;
		}
		
		if (this.activeAnimations == null) {
			this.activeAnimations = new ArrayList<Animation<IControl>>();
		}
	
		if (!this.activeAnimations.contains(animation)) {
			this.activeAnimations.add(animation);
		}
	}
	
	@Override
	public void init(AxezContext context) {
		if (this.visual != null) {
			this.visual.init(context);
		}
	}
	
	@Override
	public void draw(AxezContext context) {
		if (this.activeAnimations != null) {
			for(Iterator<Animation<IControl>> i = this.activeAnimations.iterator(); i.hasNext(); ) {
				Animation<IControl> animation = i.next();
				animation.animate(SystemClock.uptimeMillis(), this);
				if (animation.ended()) {
					i.remove();
				}
			}
		}
		
		final boolean hasVisual = this.visual != null;
		final boolean isPickable = (this.boundingShape != null);
		final boolean rayTestRequired = context.needRayTest && isPickable;
		final boolean needMatrixCalculation = hasVisual || rayTestRequired;
		
		RenderMatrices renderMatrices = context.getRenderMatrices();
		if (needMatrixCalculation) {
			renderMatrices.pushModelViewMatrix();
			this.getTransformation().applyModelViewMatrix(renderMatrices);
		}

		try {
			if (rayTestRequired) {
				boolean coordinatesFound = renderMatrices.getPickRayCoordinates(
						context.values, AxezContext.CAMERA_PICK_COORDINATES_OFFSET, 
						context.values, AxezContext.PICK_RAY_COORDINATES_OFFSET);
				if (coordinatesFound) {
					float distance = this.boundingShape.getIntersectionDistance(context.values, AxezContext.PICK_RAY_COORDINATES_OFFSET);
					context.reportPickedObject(distance, this);
				}
			}
			
			if (hasVisual) {
				this.visual.draw(context);
			}
		} finally {
			if (needMatrixCalculation) {
				renderMatrices.popModelViewMatrix();
			}
		}
	}
	
	public final Transformation getTransformation() {
		return transformation;
	}

	@Override
	public void onTouchEvent(MotionEvent event) {
		switch (event.getAction() & MotionEvent.ACTION_MASK) {
		case MotionEvent.ACTION_DOWN:
			this.touchEventTracker = TouchEventTracker.obtain();
			this.touchEventTracker.startEvent(event.getX(), event.getY());
			this.onTouchDown();
			return;
		case MotionEvent.ACTION_MOVE:
			if (this.touchEventTracker != null) {
				// we only care for events that started inside the control
				this.touchEventTracker.continueEvent(event.getX(), event.getY());
				
				if (this.touchEventTracker.getEventType() == TouchEventTracker.EVENT_TYPE_SWIPE) {
					this.onSwipe();
				}
				
				return;
			}
			break;
		case MotionEvent.ACTION_UP:
			if (this.touchEventTracker != null) {
				// we only care for events that started inside the control
				this.touchEventTracker.endEvent(event.getX(), event.getY());
				
				if (this.touchEventTracker.getEventType() == TouchEventTracker.EVENT_TYPE_CLICK) {
					this.onClick();
				} else {
					this.onSwipe();
				}

				this.touchEventTracker.recycle();
				this.touchEventTracker = null;
			}
			break;
		}
	}
	
	public String getTag() {
		return tag;
	}

	public ControlBase setTag(String tag) {
		this.tag = tag;
		return this;
	}

	public ControlBase setOnClickListener(OnClickListener onClickListener) {
		this.mOnClickListener = onClickListener;
		return this;
	}
	
	protected void onTouchDown() {
	}
	
	protected void onClick() {
		final OnClickListener listener = mOnClickListener;
		if (listener != null) {
			listener.onClick(this);
		}
	}
	
	protected void onSwipe() {
	}

}
